#include "node.h"
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <string.h>
/*
 Function: Adds node to the tree at correct position in the tree.
 Params: root: Root of the tree; node: Node which needs to be inserted.
 Returns: This function does not return any value.
*/
void addNode(Node* root, Node node)
{
	if(!root->children.empty())
	{
		for(int i = 0; i < (int)root->children.size() ;i++)
		{
			addNode(&root->children[i], node);
		}
	}
	if(root->pid == node.ppid)
	{
		root->children.push_back(node);
	}
} 
/*
 Function: Displays a tree node on the console.
 Params: root: Root of the tree; indent: Indentation for this node of the tree.
 Returns: This function does not return any value.
*/
void displayNode(Node* root,string indent)
{
	stringstream pidStr, ppidStr;
	pidStr << root->pid;
	ppidStr <<root->ppid;
	if(root->pid != 0)
	{
		cout << indent << endl;
		cout << indent << "__" << root->command + " (PID: " + pidStr.str() + " PPID: " + ppidStr.str() + ")" << endl;
	}
	else
	{
		cout << root->command << endl;		
	}
	if(!root->children.empty())
	{
		for(int i = 0; i < (int)root->children.size(); i++)
		{
			displayNode(&root->children[i], "  |" + indent);
		}
	}
}
/*
 Function: Creates tree from nodes.
 Params: root: Root for the tree; nodes: Vector of nodes to create tree.
 Returns: This function does not return any value.
*/
void createTree(Node* root, vector<Node>* nodes)
{
	for(int n = 0; n < (int)nodes->size(); n++)
	{

		if((int)root->children.size() == 0)
		{
			root->children.push_back((*nodes)[n]);
		}
		else
		{
			addNode(root, (*nodes)[n]);
		}
	}
}
/*
 Function: Converts a string to Node.
 Params: line: Line item from ps command; pidColumn: Column number for PID
	 ppidColumn: Column number for PPID; cmdColumn: Column number for CMD.
 Returns: Node created from string.
*/
Node convertStringToNode(string line, int pidColumn,int ppidColumn, int cmdColumn)
{
	istringstream psLineItem(line); 
	Node n;
	int pid = -1, ppid = -1, columnNo = 0;
	string value, cmdValue;
	while(psLineItem.good())
	{
		getline(psLineItem, value,' ');	

		if(columnNo == pidColumn)
		{
			pid = atoi(value.c_str());
		}
		if(columnNo == ppidColumn)
		{			
			ppid = atoi(value.c_str());

		}
		if(value != "" && value.find("(") != 0)			
			columnNo++;
		if(columnNo == cmdColumn)
		{			
			while(psLineItem.good())
			{
				getline(psLineItem, value,' ');
				cmdValue.append(" ");	
				cmdValue.append(value);			
			}
		}			
	}

	n.pid = pid;
	n.ppid = ppid;
	n.command = cmdValue.substr(0, strlen(cmdValue.c_str()) - 1);
	return n;
}
/*
 Function: Converts strings to Nodes.
 Params: psLineItems: Line items from ps command; totalLines: Number of lines read.
 Returns: Vector of nodes created from string.
*/
vector<Node> convertStringsToNodes(string* psLineItems, int totalLines)
{
	int pidColumn = -1, ppidColumn = -1, cmdColumn = -1, totalColumns = 0;
	istringstream header(psLineItems[0]);
	while(header.good())
	{
		string columnName;
		getline(header, columnName, ' ');

		if(columnName == "PID")
			pidColumn = totalColumns ;
		if(columnName == "PPID")
			ppidColumn = totalColumns;
		if(columnName.find("CMD") != string::npos)
			cmdColumn = totalColumns;
			
		if(columnName != "")
			totalColumns++;	
	}
	vector<Node> nodes;
	for(int line = 1; line < totalLines; line++)
	{
		Node n = convertStringToNode(psLineItems[line], pidColumn, ppidColumn, cmdColumn);
		
		if((int)nodes.size() != 0)
		{
			for(int t = 0; t < (int)nodes.size(); t++)
			{
				if(nodes[t].pid > n.pid)
				{
					vector<Node>::iterator it = nodes.begin();
					nodes.insert(it + t, n);
					break;
				}
				else if(t == (int) nodes.size() - 1 && n.pid != 0)
				{
					nodes.push_back(n);
					break;
				}			
			}
		}
		else
			nodes.push_back(n);		
	}
	if(nodes[0].pid == 0)
		nodes.erase(nodes.begin());
	return nodes;
}
/*
 Function: Reads line items to stdin
 Params: lineItems: Array of line items
 Returns: Number of line items read from stdin
*/
int readStdIn(string* lineItems)
{
	int lineNumber = 0;
	char ch;
	clearerr(stdin);

	for(int j = 0; (ch=getc(stdin)) != EOF ; j++)
	{
		lineItems[lineNumber] += ch;
		if(ch == '\n')
		{
			lineNumber++;
		}
	}
	return lineNumber;
}

void assertGreaterThanInt(const int expected, const int actual);

void testAddNode();
void testCreateTree();
void testConvertStringToNode();
void testConvertStringsToNodes();

/*
 Function: Main entry point for this program.
*/
int main(int argc, char* argv[])
{
	if(argc == 2 && strcmp(argv[1], "--test") == 0 )
	{
		printf("Starting tests...\n");

		testAddNode();	
		testCreateTree();
		testConvertStringToNode();
		testConvertStringsToNodes();
	
		printf("All tests passed.\n");
	}
	Node root(0, 0,"Root");
	string psLineItems[1000];
	int totalLines = readStdIn(psLineItems);

	vector<Node> nodes = convertStringsToNodes(psLineItems, totalLines);

	createTree(&root, &nodes);
	cout<<"Displaying tree:\n" <<endl;
	displayNode(&root, "");
	return 1;
}

/**************************** Test cases ***********************************/
void assertGreaterThanInt(string testName, const int expected, const int actual);
void assertEqualsInt(string testName, const int expected, const int actual);

void testAddNode()
{
	string linesItem = "0 S  1000 15650 0  0  80   0 -  6736 wait   pts/0    00:00:00 bash";
	int pidColumn = 3;
	int ppidColumn = 4;
	int cmdColumn = 13;
	Node node = convertStringToNode(linesItem, pidColumn, ppidColumn, cmdColumn);
	Node root(0, 0,"Root");

	addNode(&root, node);
	assertGreaterThanInt("testAddNode", 0, root.children.size());
} 

void testCreateTree()
{
	string linesItems[5];
	linesItems[0] = "F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD";
	linesItems[1] = "0 S  1000 15650 15630  0  80   0 -  6736 wait   pts/0    00:00:00 bash";
	linesItems[2] = "0 R  1000 21465 15650  0  80   0 -  3542 -      pts/0    00:00:00 ps";
	int totalLines = 3;	
	Node root(0, 0,"Root");

	vector<Node> nodes = convertStringsToNodes(linesItems, totalLines);
	createTree(&root, &nodes);
	assertGreaterThanInt("testCreateTree", 0, root.children.size());
}

void testConvertStringToNode()
{
	string linesItem = "0 S  1000 15650 15630  0  80   0 -  6736 wait   pts/0    00:00:00 bash";
	int pid = 15650;
	int pidColumn = 3;
	int ppidColumn = 4;
	int cmdColumn = 13;
	Node n = convertStringToNode(linesItem, pidColumn, ppidColumn, cmdColumn);
	assertEqualsInt("testConvertStringToNode", pid, n.pid);
}
void testConvertStringsToNodes()
{
	string linesItems[5];
	linesItems[0] = "F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD";
	linesItems[1] = "0 S  1000 15650 15630  0  80   0 -  6736 wait   pts/0    00:00:00 bash";
	linesItems[2] = "0 R  1000 21465 15650  0  80   0 -  3542 -      pts/0    00:00:00 ps";
	int totalLines = 3;	
	vector<Node> nodes = convertStringsToNodes(linesItems, totalLines);
	
	assertEqualsInt("testConvertStringsToNodes", (int)nodes.size(), totalLines-1);	
}

void assertEqualsInt(string testName, const int expected, const int actual)
{
	if(expected != actual)
	{
		printf("Expected <%d> but was <%d> in %s.\n", expected, actual, testName.c_str());
		exit(1);	
	}
}

void assertGreaterThanInt(string testName, const int expected, const int actual)
{
	if(expected >= actual)
	{
		printf("Expected greater than <%d> but was <%d> in %s.\n", expected, actual, testName.c_str());
		exit(1);	
	}
}

